package base.thread_demo.demo11;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 双线程间通信案例：打印出1-100，使用两个线程轮番打印输出
 * Lock 版
 * Lock接口：出现替代了同步代码块或者同步函数，将同步的隐式锁(synchronized代码块或者函数)
 * 操作变成现实锁操作，同时更为灵活。可以一个锁桑加上多组监视器。
 *
 * lock():获取锁。
 * unlock():释放锁，通常需要定义finally代码块中。
 * Condition接口：出现替代了Object中的wait notify notifyAll方法。
 * 将这些监视器方法单独进行了封装，变成Condition监视器对象。可以任意锁进行组合。
 * await();
 * signal();
 * signalAll();
 */
public class CommunicationDemo1 {
    public static void main(String[] args) {
        Num num = new Num(100);
        NumRunnable runnable = new NumRunnable(num);

        new Thread(runnable, "t1").start();
        new Thread(runnable, "t2").start();
    }
}

class NumRunnable implements Runnable{
    private static final Logger log = LoggerFactory.getLogger(NumRunnable.class);
    private Num num;
    private int count=1;
    Lock lock = new ReentrantLock();

    public NumRunnable(Num num){
        this.num = num;
    }

    @Override
    public void run() {
        Condition condition = lock.newCondition();
        while (true){
            lock.lock();
            try {
                condition.signal();//在比较之前先唤醒对方.
                if(count <= num.getNumber()){
                    num.print(count);
                    count++;
                    condition.await();// 如果在这里睡眠,那么finally块就不会执行
                }else{
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//            finally {
//                log.info("finally");
//                lock.unlock();
//                try {
//                    condition.await();// 自己进入睡眠,确保让对方线程运行
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
        }
    }
}

class Num {
    private static final Logger log = LoggerFactory.getLogger(Num.class);
    private int number;
    public Num(int number){
        this.number = number;
    }

    public void print(int count){
        log.info("{}->{}", Thread.currentThread().getName(), count);
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}
